Skip to content

fix(sdk): fail closed on hosted extension dispatch - #549

Merged
kjgbot merged 3 commits into
mainfrom
fix/flow-extension-event-dispatch
Sep 22, 2026
Merged

kjgbot merged 3 commits into
mainfrom
fix/flow-extension-event-dispatch

Conversation

@miyaontherelay

@miyaontherelay miyaontherelay commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Problem

Schema-2 extensions can install and contribute provider watch rules, but their entries are ordinary authored JavaScript. The runtime does not yet isolate f.run, helpers, MCP, harnesses, or direct Node access to FlowExtensionPermissions. The prior head selected a handler from user-controlled input.event and passed it the base flow context; independent review correctly rejected that design.

Babysitter remains an optional extension of Software Garden, not a second Recommended Flow. This PR now establishes the safe boundary only; it does not claim that Babysitter handlers can execute.

Change

  • remove input-derived handler selection; direct-run JSON cannot opt into extension execution
  • accept dispatch metadata only through a separate executor option branded with a non-serializable authority symbol after the host verifies an integration delivery
  • validate provider, event type, and delivery identity and fail closed on forged, serialized, malformed, duplicate, or generic/action-overlapping dispatch
  • refuse a matched handler with plugin_unsupported before either the base or extension body runs because manifest permission isolation is not implemented (gate 8 / flows: AgentOptions has no permissions field in TypeScript, and it isn't enforced yet anywhere #442)
  • document the current refusal boundary instead of claiming permissions are enforced
  • keep the exhaustive preflight refusal taxonomy guarded

Verification

$ npm run typecheck --prefix packages/sdk
> @relayflows/sdk@2.0.25 typecheck
> tsc --noEmit && tsc -p tsconfig.type-tests.json

$ npm run build --prefix packages/sdk
> @relayflows/sdk@2.0.25 build
> tsc && node scripts/make-cli-executable.mjs

$ npm run typecheck:tests --prefix packages/sdk
> @relayflows/sdk@2.0.25 typecheck:tests
> tsc -p tsconfig.tests.json

After the same built-local-Surface override used by the Cloud runtime artifact workflow:

$ cd packages/sdk && ./node_modules/.bin/vitest run tests/preflight.test.ts tests/authored-flow.test.ts tests/flow-extension-compose.test.ts
 Test Files  3 passed (3)
      Tests  115 passed (115)
   Duration  11.68s

This PR does not publish packages, update a catalog, implement session lineage, or enable merge/write capabilities. Actual Babysitter execution remains blocked until an isolated/native existing-session path proves and enforces its authority boundary.

Session-Id: 01a0c4a6-dd65-7ce1-a90e-de1b0b4e86c3

Session-Id: 01a0c4a6-dd65-7ce1-a90e-de1b0b4e86c3
@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Advanced

Run ID: cb9c2491-048b-4e35-a50b-4149d55cf17c


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Session-Id: 01a0c4a6-dd65-7ce1-a90e-de1b0b4e86c3
@miyaontherelay

Copy link
Copy Markdown
Contributor Author

CI follow-up at 989f7483: the failure was the closed preflight refusal-taxonomy guard. The dispatcher introduced plugin_event_ambiguous, but the exhaustive corpus did not yet exercise that code path. The corpus now constructs two installed-extension handler surfaces claiming the same normalized github pull_request.labeled event, calls the real dispatcher, captures the typed PluginError, and keeps the exact taxonomy assertion intact.

Literal targeted verification:

$ npm run typecheck --prefix packages/sdk
> @relayflows/sdk@2.0.25 typecheck
> tsc --noEmit && tsc -p tsconfig.type-tests.json

$ npm run build --prefix packages/sdk
> @relayflows/sdk@2.0.25 build
> tsc && node scripts/make-cli-executable.mjs

$ npm run typecheck:tests --prefix packages/sdk
> @relayflows/sdk@2.0.25 typecheck:tests
> tsc -p tsconfig.tests.json

$ cd packages/sdk && ./node_modules/.bin/vitest run tests/preflight.test.ts tests/authored-flow.test.ts tests/flow-extension-compose.test.ts
 Test Files  3 passed (3)
      Tests  108 passed (108)
   Duration  12.70s

I also ran the entire SDK suite locally. The original taxonomy failure is gone, but that local invocation is not green: it ran without the workflow's pinned Bun 1.4.0 and under enough concurrency to trip 34 existing 5-second timing/lease tests (12 failed | 151 passed | 1 skipped). I am not claiming that as a pass; the replacement canonical CI run is the authority for the full gate.

@miyaontherelay
miyaontherelay requested review from khaliqgant and removed request for khaliqgant September 22, 2026 07:13
Session-Id: 01a0c4a6-dd65-7ce1-a90e-de1b0b4e86c3
@miyaontherelay miyaontherelay changed the title feat(sdk): dispatch hosted extension handlers fix(sdk): fail closed on hosted extension dispatch Sep 22, 2026
@miyaontherelay

Copy link
Copy Markdown
Contributor Author

Security follow-up at exact head 482d2562df158b8f49e6b924c94f1f8e1c9f5c16:

  • direct-run input.event no longer participates in selection; the regression proves a crafted matching envelope runs only the base body
  • verified-delivery dispatch is out-of-band and branded with a non-serializable symbol; a JSON round-trip loses authority and is refused
  • a valid matching dispatch refuses with plugin_unsupported before either base or extension body runs because manifest permissions remain unenforced
  • duplicate exact handlers and generic/action overlap refuse with plugin_event_ambiguous
  • malformed provider/event/delivery/provenance descriptors refuse with plugin_event_unroutable
  • docs no longer claim handler execution or permission enforcement

Literal local verification after the workflow-equivalent built-local-Surface override:

$ npm run typecheck --prefix packages/sdk
> @relayflows/sdk@2.0.25 typecheck
> tsc --noEmit && tsc -p tsconfig.type-tests.json

$ npm run build --prefix packages/sdk
> @relayflows/sdk@2.0.25 build
> tsc && node scripts/make-cli-executable.mjs

$ npm run typecheck:tests --prefix packages/sdk
> @relayflows/sdk@2.0.25 typecheck:tests
> tsc -p tsconfig.tests.json

$ cd packages/sdk && ./node_modules/.bin/vitest run tests/preflight.test.ts tests/authored-flow.test.ts tests/flow-extension-compose.test.ts
 Test Files  3 passed (3)
      Tests  115 passed (115)
   Duration  11.68s

Canonical CI at this exact head:

validate             pass  15s
packed-consumer      pass  34s
guard                pass   5s
linux-x64-artifact   pass  8m18s
Cursor Bugbot        pass  4m16s
CodeRabbit           pass

Fresh human review is now requested. This remains unmerged and unreleased.

@kjgbot
kjgbot merged commit 190aa1f into main Sep 22, 2026
9 checks passed
kjgbot pushed a commit that referenced this pull request Sep 22, 2026
Add extensions/babysitter, a schema-2 extension on software-factory whose
eleven GitHub handlers turn Cloud's normalized delivery descriptor into one
cloud:babysitter-turn queue call:
f.capabilities.cloud.babysitterTurn.queue({ delivery }). It sends no
findings, head, label, session, lineage, or config. Unknown input, receipts
other than queued|duplicate, rejections, and a missing capability fail the
run. The manifest matches the #550 exporter permission shape.

#549's refusal is unchanged: hosted execution still refuses the handler
with plugin_unsupported. compat requires the surface release after 2.0.25.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Session-Id: 7d116c0d-6756-4905-ace7-1235559066ed

Co-authored-by: agentrelaybot <agentrelaybot@agentrelay.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants